Skip to content

feat!: wire the list_proposals and list_collections tasks - #1803

Merged
bokelley merged 2 commits into
nextfrom
claude/issue-1645-list-proposals-collections
Nov 2, 2026
Merged

bokelley merged 2 commits into
nextfrom
claude/issue-1645-list-proposals-collections

Conversation

@bokelley

@bokelley bokelley commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Serves the experimental AdCP 3.3 reads list_proposals (media_buy.open_opportunities) and list_collections (media_buy.collection_catalog) end to end, against the pinned 3.3.0-beta.3 bundle. The client methods, multi-agent client, ADCPHandler stubs, MCP/A2A adapters, CLI and MCP tool list were already registered on next; this PR adds the seller framework side and the gaps around it.

  • Decisioning hooks. New OpenOpportunitiesPlatform and CollectionCatalogPlatform Protocols, exported from adcp.decisioning and delegated by PlatformRouter. PlatformHandler advertises and serves:

    • list_proposals when media_buy.lifecycle_tools lists it or media_buy.open_opportunities.list_proposals is true;
    • list_collections when experimental_features lists media_buy.collection_catalog.

    A method with no declaration is not advertised. A declaration with no method fails validate_platform at boot. list_collections resolves without an account. Both refuse a caller below 3.3 with UNSUPPORTED_FEATURE, including on a server whose handlers run on an older release. adcp.decisioning.capabilities now exports OpenOpportunities and LaterProposals.

  • Cross-version refusal. The converter has no older equivalent for either task. The dispatcher now answers a task the caller's release does not define with UNSUPPORTED_FEATURE and details: {task, adcp_version}, where it used to answer VERSION_UNSUPPORTED. The spec requires this for list_collections. The client already refused such a call before transport. Tests cover the server, the client (MCP and A2A, no dispatch) and the converter. A 3.2 get_adcp_capabilities caller loses list_proposals from lifecycle_tools and loses open_opportunities, and each drop is recorded as a loss.

  • Response envelope. _response_envelope.py treats both tasks like list_products. In the 3.3 prereleases before beta.3, their roots are closed and have no status, and beta.3 composes the protocol envelope. The bundled beta.1 / beta.2 list_collections examples now pass the SDK's own validation.

  • proposal.created. WebhookSender.prepare_proposal_created / send_proposal_created build a schema-validated payload, and notification_id defaults to and must equal proposal_id. Agent-level notification configs now treat proposal.created, proposal.offered, collection_card.*, collection_catalog.bulk_change and creative.staged_update_changed as account-anchored. A drift-guard test checks the classification against the enum.

  • MCP tool stubs carry the 3.3 request fields and the correct capability names. The media-buy default trust scope covers both reads. Docs updated: inventory, migration guide, proposal-surfaces.md, cross-version-conversion.md, AGENTS.md.

Deferred

  • Serving list_proposals from ProposalStore. The store keeps the legacy get_products proposal payloads that recipes hydrate from. It does not keep canonical snapshots with disposition / origin, and it cannot list by account. Documented in proposal-surfaces.md.
  • Reference-seller wiring. Not cheap without store-backed snapshots.
  • Prepare/send helpers for proposal.offered and collection_card.*. Not added.

Writes outside the stated scope

src/adcp/_notification_envelope.py and src/adcp/webhook_sender.py have small additive changes for proposal.created. src/adcp/types/capabilities.py gains two re-exports. Black reformatted one unrelated expression in server/principal.py.

Test plan

  • tests/test_list_proposals_collections.py (new)
  • updated test_notification_preparation.py, test_cross_version_conversion.py, test_decisioning_handler_shims.py
  • make lint, make typecheck-all
  • full suite -n 3: all pass except test_notification_outbox_artifacts.py::test_installed_public_import_schema_and_typing, which errors identically on pristine next in this sandbox because it builds a wheel

BREAKING CHANGE: a server asked for a task its caller's release does not define now fails with UNSUPPORTED_FEATURE instead of VERSION_UNSUPPORTED. validate_platform fails when a platform declares list_proposals or media_buy.collection_catalog without the method. Agent-level notification registrations of proposal.created, proposal.offered, collection_card.*, collection_catalog.bulk_change or creative.staged_update_changed require all_authorized_accounts: true.

Closes #1645

🤖 Generated with Claude Code

Serve the experimental AdCP 3.3 reads from a DecisioningPlatform and close
the gaps around them.

- OpenOpportunitiesPlatform / CollectionCatalogPlatform hooks. PlatformHandler
  serves list_proposals when media_buy.lifecycle_tools lists it or
  open_opportunities.list_proposals is true, and list_collections when
  experimental_features lists media_buy.collection_catalog. Undeclared methods
  are not advertised; a declaration without a method fails validate_platform.
  list_collections resolves without an account. Both refuse a caller below
  3.3 with UNSUPPORTED_FEATURE.
- The dispatcher answers a task the caller's release does not define with
  UNSUPPORTED_FEATURE (details task, adcp_version) instead of
  VERSION_UNSUPPORTED, as the spec requires for list_collections.
- The response-envelope helper treats both tasks like list_products: the
  3.3 prereleases before beta.3 close their roots without status, so the
  bundled beta.1 / beta.2 list_collections examples now validate.
- proposal.created: WebhookSender.prepare_proposal_created /
  send_proposal_created, schema-validated, notification_id bound to
  proposal_id. Agent-level notification configs classify the 3.3
  account-anchored types, with a drift guard against the enum.
- MCP tool stubs carry the 3.3 request fields; capability exports
  OpenOpportunities and LaterProposals; media-buy default trust scope
  covers both reads.

BREAKING CHANGE: a server asked for a task its caller's release does not
define now fails with UNSUPPORTED_FEATURE instead of VERSION_UNSUPPORTED.
validate_platform fails when a platform declares list_proposals or
media_buy.collection_catalog without the method. Agent-level notification
registrations of proposal.created, proposal.offered, collection_card.*,
collection_catalog.bulk_change or creative.staged_update_changed require
all_authorized_accounts true.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…_collections

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Approve

Approve — clean review, no blocking or medium findings.

This PR serves the AdCP 3.3 experimental reads (list_proposals / list_collections) on PlatformHandler with declaration gating, boot validation, and UNSUPPORTED_FEATURE refusal of sub-3.3 callers, plus proposal.created webhook helpers.

Checked:

  • The breaking error-code change (VERSION_UNSUPPORTED → UNSUPPORTED_FEATURE for a task the caller's release does not define) is correctly shipped as feat! with migration notes (MIGRATION_v9_to_v10.md), satisfying the semver-signal gate.
  • No-double-missing reported guard (dispatch.py:971-978), task-not-defined narrowing (mcp_tools.py:3597-3615), optional-account handling (handler.py:1740), and notification_id==proposal_id ordering after schema validation (_notification_envelope.py:94-95).
  • Comprehensive test coverage (new test_list_proposals_collections.py, 368 lines, including drift guard on account-anchored notification types).

High-risk flag is true only because the diff touches src/adcp/decisioning/** and src/adcp/server/** — all entries are (modified) or (added), none (deleted), and the reviewer surfaced no medium-or-higher concerns on the modified files. No escalation trigger. gated_paths is false; no author team gate. Falls through to row 9.

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Approve

Approve — subsequent pass, docstring-only delta since the prior approve.

The only change since the last approved run (b0ab5b1) is docstring edits in src/adcp/client.py for list_collections/list_proposals (single- and multi-agent): the feature id is renamed to media_buy.collection_catalog, the pre-send UNSUPPORTED_FEATURE refusal is documented, and list_proposals is reworded to "proposal snapshots and their dispositions." The reviewer confirms all text matches already-shipped behavior (experimental_reads.refuse_caller_below_release, the renamed capability) — no behavior change, no public surface touched, no over-threshold delta file.

Decision-table walk: no critical/high/medium findings (row 1 n/a). gated_paths is false and review_decision is APPROVED anyway (row 2 n/a). high_risk is true but all reasons are (modified)/(added) with zero findings — no (deleted), no medium on a modified sensitive file, so rows 3–5 do not fire. Prior decision was approve, not escalate (row 6 n/a). No no-auto-approve team match (row 7 n/a). Zero medium findings, so row 8 n/a. Falls through to row 9 → approve.

Nothing actionable surfaced.

@bokelley
bokelley merged commit 0dc1b21 into next Nov 2, 2026
41 checks passed
@bokelley
bokelley deleted the claude/issue-1645-list-proposals-collections branch November 2, 2026 02:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant